package com.suemi.rpc.codec;

import com.suemi.rpc.entity.MessageType;
import com.suemi.rpc.entity.RpcMessage;
import com.suemi.rpc.util.SerializationUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.util.zip.CRC32;

public class RpcMessageDecoder extends LengthFieldBasedFrameDecoder {

  public RpcMessageDecoder() {
    super(1024 * 1024, 8, 4);
  }

  @Override
  protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
    ByteBuf frame = (ByteBuf) super.decode(ctx, in);
    if (frame == null) return null;

    long crcCode = frame.readLong();
    frame.markReaderIndex();
    byte[] tmp = new byte[frame.readableBytes()];
    frame.readBytes(tmp);
    CRC32 crc = new CRC32();
    crc.update(tmp);
    if (crcCode != crc.getValue())
      throw new IllegalStateException("Crc invalid message!");

    frame.resetReaderIndex();

    RpcMessage.RpcMessageBuilder builder = new RpcMessage.RpcMessageBuilder();

    builder.crc(crcCode);
    builder.length(frame.readInt());
    builder.sender(frame.readLong());
    MessageType type = MessageType.from(frame.readByte());
    builder.type(type);

    int attachmentSize = frame.readInt();
    if (attachmentSize > 0) {
      int keySize;
      byte[] keyArray;
      String key;
      byte[] valueArray;

      for (int i = 0; i < attachmentSize; ++i) {
        keySize = frame.readInt();
        keyArray = new byte[keySize];
        frame.readBytes(keyArray);
        key = new String(keyArray, "UTF-8");
        valueArray = new byte[frame.readInt()];
        frame.readBytes(valueArray);
        builder.attachment(key, new String(valueArray, "UTF-8"));
      }
    }

    int bodyLength = frame.readInt();
    if (bodyLength > 0) {
      byte[] bodyArray = new byte[bodyLength];
      frame.readBytes(bodyArray);
      builder.body(SerializationUtil.deserialize(bodyArray, type.getClass()));
    }

    return builder.build();

  }
}
